//============================================================================================
/**
 * @file	field_camera.c
 * @brief	フィールドカメラ
 * @author	GAME FREAK inc.
 */
//============================================================================================
#include "common.h"
#include "camera.h"
#include "system/bmp_list.h"
#include "system/bmp_menu.h"
#include "worldmap.h"
#include "fieldsys.h"

#include "field_camera.h"

#define FIELD_CAMERA_DELAY	(6)
#define FIELD_CAMERA_TRACE_BUFF	(FIELD_CAMERA_DELAY+1)

typedef	struct	{
	fx32			Distance;
	CAMERA_ANGLE	Angle;
	u16				View;
	u16				PerspWay;
	fx32			Near;
	fx32			Far;
	VecFx32			Shift;
}FLD_CAMERA_PARAM;

#include "field_camera.dat"
#if 0
static const FLD_CAMERA_PARAM FieldCameraData[] = {
	{							//	0	通常
		0x29aec1,
		{
			-0x201e/*-0x29fe*/,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x05c1,
		FX32_ONE * 150,
		FX32_ONE * 900,
		{0,0,-0x4c000}
	},
	{							//　1　飛行ジム
		0x1813ae,
		{
			-0x1f9d/*-0x21dd*/,0,0
		},
		GF_CAMERA_PERSPECTIV,
		/*0x0a81*/0x0a41,
		FX32_ONE * 115,
		FX32_ONE * 1220,
		{0,0,0}
	},
/**
	{							//	1	水ジム
		0x29aec1,
		{
			-0x309e,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x05c1,
		FX32_ONE * 150,
		FX32_ONE * 900
	},
*/
	{							//	2	最初の湖
		0x20374c,
		{
			-0x26de,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x770,
		FX32_ONE * 150,
		FX32_ONE * 900
	},
	{							//	3	鋼ジム
		0x29aec1,
		{
			-0x29fe,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x05c1,
		FX32_ONE * 150,
		FX32_ONE * 900
	},
	{							//	4	屋内（正射影）
		0x61b89b,
		{
			-0x239e,0,0
		},
		GF_CAMERA_ORTHO,
		0x0281,
		FX32_ONE * 150,
		FX32_ONE * 1735
	},

	{							//	5	テンガンザン山頂
		0x13c805,
		{
			-0x29fd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0c01,
		FX32_ONE * 10,
		FX32_ONE * 1008
	},

	{							//	6	テンガンザン外回り1
		0x3628df,
		{
			-0x33fd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0481,
		FX32_ONE * 115,
		FX32_ONE * 1221
	},

	{							//	7	テンガンザン外回り2
		0x29aec1,
		{
			-0x29fd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x05c1,
		FX32_ONE * 153,
		FX32_ONE * 1031
	},

	{							//	8	火山奥
		0x296ec1,
		{
			-0x321d,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0701,
		FX32_ONE * 150,
		FX32_ONE * 1034
	},

	{							//	9	岩ジム
		0x1659ac,
		{
			-0x1cdd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0ab0,
		FX32_ONE * 150,
		FX32_ONE * 900
	},

	{							//	10	格闘ジム
		0x4b25b1,
		{
			-0x2b3d,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0341,
		FX32_ONE * 150,
		FX32_ONE * 1746
	},

	{							//	11	ナギサシティ/223番水道/チャンピオンリーグ
		0x2a3d55,
		{
			-0x291d,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x05c1,
		FX32_ONE * 230,
		FX32_ONE * 1127
	},

	{							//	12	洞窟
		0x23e93f,
		{
			-0x2cfd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x06c1,
		FX32_ONE * 150,
		FX32_ONE * 900
	},

	{							//	13	鉱山
		0x20374c,
		{
			-0x21fd,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x0770,
		FX32_ONE * 150,
		FX32_ONE * 900
	},

	{							//	14	はじまりのま
		0xa9765,
		{
			-0x37bc,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x1501,
		FX32_ONE * 10,
		FX32_ONE * 1008
	},

	{							//	15	エイチ湖専用カメラ
		0x28dedf,
		{
			-0x26de,0,0
		},
		GF_CAMERA_PERSPECTIV,
		0x5f0,
		FX32_ONE * 150,
		FX32_ONE * 900
	},
};
#endif


//==============================================================================
/**
 * フィールド上で行うカメラ設定
 *
 * @param   Hero			主にエリア切り替え時に対応したカメラが設定される
 * @param	repw			フィールドシステムポインタ
 * @param   AreaCamera		主にエリア切り替え時に対応したカメラが設定される
 * @param	inDelayFlg		ｙ軸遅延を行うかどうかのフラグ
 *
 * @retval  none
 */
//==============================================================================
void FieldCameraInit(	const VecFx32 *inTarget,
						FIELDSYS_WORK * repw,
						const int AreaCamera,
						const BOOL inDelayFlg)
{

	//カメラセット
	const VecFx32 *target = inTarget;
	const FLD_CAMERA_PARAM * camera_param = &FieldCameraData[AreaCamera];

	GF_ASSERT(AreaCamera < NELEMS(FieldCameraData));

	repw->camera_ptr = GFC_AllocCamera(HEAPID_FIELD);//フィールドカメラメモリ確保

	//カメラセット
	GFC_InitCameraTDA( target,
				camera_param->Distance,	// 距離
				&camera_param->Angle,	//
				camera_param->PerspWay,	// 投射影の角度
				camera_param->View,
				TRUE,
				repw->camera_ptr
				);
	//カメラの有効化
	GFC_AttachCamera(repw->camera_ptr);
	//ニア・ファー設定
	GFC_SetCameraClip(camera_param->Near, camera_param->Far,repw->camera_ptr);
	//カメラシフト
	GFC_ShiftCamera(&camera_param->Shift, repw->camera_ptr);

	if (inDelayFlg){
		//カメラトレースデータセットアップ
		GFC_AllocTraceData(	FIELD_CAMERA_TRACE_BUFF, FIELD_CAMERA_DELAY,
							CAM_TRACE_MASK_Y, HEAPID_FIELD, repw->camera_ptr);
	}

	repw->SmlsCamCntPtr = SMLS_CamCnt_Init(HEAPID_FIELD);
	SMLS_CamCnt_Setup(repw, AreaCamera);
}

//==============================================================================
/**
 * フィールド上カメラの解放
 *
 * @param   repw		フィールドシステムポインタ
 *
 * @retval  none
 */
//==============================================================================
void FieldCameraEnd(FIELDSYS_WORK * repw)
{
	SMLS_CamCnt_Delete(repw->SmlsCamCntPtr);	//シームレスカメラコントローラ解放

	GFC_PurgeCamera();						//カメラの切り離し（無効化）

	GFC_FreeTraceData(repw->camera_ptr);	//カメラトレースデータ解放
	GFC_FreeCamera(repw->camera_ptr);		//フィールドカメラメモリ解放
}

///--------------------------------------------------------------------------------------------
//シームレスカメラ変更処理関連(カメラシフトとアングル変更のみ)
//

typedef struct CAM_PARAM_tag
{
	u16 Angle;		//X軸回転アングル
	u16 LotDir;		//回転方向
	VecFx32 Length;	//シフト変更値
}CAM_PARAM;

typedef struct SMLS_CAM_CNT_tag
{
	GF_CAMERA_PTR CameraPtr;
	u8 TotalSync;			//変更所要シンク数
	u8 NowSync;				//現在シンク数
	u8 Seq;					//変更処理シーケンサ
	u8 ParamID;				//変更パラメータID

	CAM_PARAM	DstParam;	//変更後のパラメータ
	CAM_PARAM	NowParam;	//現在のパラメータ

	BOOL Req;				//変更リクエスト有無

	u16	SrcAngle;			//元のXアングル
	u16 Reverse;			//逆再生フラグ

	int AreaCamera;

}SMLS_CAM_CNT;

typedef struct SMLS_PARAM_tag
{
	CAM_PARAM CamParam;
	int TotalSync;
}SMLS_PARAM;

static const SMLS_PARAM SmlsParam[] = {
	{
		{
			-0x1a9e,0,{0, 0, -0x6c000},
		},
		24
	},
};

static void ChangeCamAngle(GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst, const u8 now_sync, const u8 total_sync );
static void ShiftCamPos(GF_CAMERA_PTR camera_ptr, const VecFx32 *inShift, const u8 now_sync, const u8 total_sync );
static fx32 GetAddVal(const fx32 inShiftVal, const u8 now_sync, const u8 total_sync);

int test_save_work = 0;

//==============================================================================
/**
 * シームレスカメラコントローラ初期化
 *
 * @param   fsys		フィールドシステムポインタ
 *
 * @retval  none
 */
//==============================================================================
SMLS_CAM_CNT_PTR SMLS_CamCnt_Init(const int inHeapID)
{
	SMLS_CAM_CNT_PTR cnt_ptr;
	int size = sizeof(SMLS_CAM_CNT);

	cnt_ptr = sys_AllocMemory(inHeapID, size);
	//メモリクリア
	MI_CpuClear8(cnt_ptr, size);
	return cnt_ptr;
}

//==============================================================================
/**
 * シームレスカメラコントローラ解放
 *
 * @param   fsys		フィールドシステムポインタ
 *
 * @retval  none
 */
//==============================================================================
void SMLS_CamCnt_Delete(SMLS_CAM_CNT_PTR CntPtr)
{
	sys_FreeMemoryEz(CntPtr);
}

//==============================================================================
/**
 * シームレスカメラコントローラセットアップ
 *
 * @param	fsys		フィールドシステムポインタ
 * @param   inAreaCamera		主にエリア切り替え時に対応したカメラが設定される
 *
 * @retval  none
 */
//==============================================================================
void SMLS_CamCnt_Setup(FIELDSYS_WORK * fsys, const int inAreaCamera)
{
	u8 param_id;
	SMLS_CAM_CNT_PTR cnt_ptr = fsys->SmlsCamCntPtr;
	//カメラポインタセット
	cnt_ptr->CameraPtr = fsys->camera_ptr;
	//エリア保存
	cnt_ptr->AreaCamera = inAreaCamera;

	//セーブワークを見て、現在カメラ変更が行われているかで、セットアップ内容を変更する
	param_id = test_save_work;

	if (param_id != 0){	//カメラ変更されている場合
		CAMERA_ANGLE angle = {0,0,0,0};
		angle.x = SmlsParam[param_id-1].CamParam.Angle;
		//変更パラメータIDセット
		cnt_ptr->ParamID = param_id;
		//カメラデータを変更後パラメータで書き換える
		GFC_SetCameraAngleRev(&angle, cnt_ptr->CameraPtr);
		GFC_ShiftCamera( &SmlsParam[param_id-1].CamParam.Length, cnt_ptr->CameraPtr);
	}
}

//==============================================================================
/**
 * カメラ変更リクエスト
 *
 * @param   CntPtr		コントローラポインタ
 * @param	inParamID	変更ID
 *
 * @retval  none
 */
//==============================================================================
void SMLS_CamCnt_Request(SMLS_CAM_CNT_PTR CntPtr, const u8 inParamID)
{
	if (inParamID == 0){
		return;
	}

	if(CntPtr->Req){		//既にリクエストがかかっている場合
		//現在の変更パラメータIDと指定パラメータIDが同一の場合、
		if (CntPtr->ParamID == inParamID){
			//変更後と変更前を逆転させてリクエスト
			CntPtr->Reverse = (CntPtr->Reverse+1)%2;
		}else{
			//別のリクエストがかかったとみなし、アサート
			GF_ASSERT(0);
		}
	}else{					//リクエストがかかっていない
		const FLD_CAMERA_PARAM * camera_param = &FieldCameraData[CntPtr->AreaCamera];
		CntPtr->ParamID = inParamID;	//パラメータID記憶
		CntPtr->Req = TRUE;				//変更をリクエスト
		//今回のカメラ変更はデフォから新カメラへの変更か？
		if(!test_save_work){	//デフォから新カメラ
			//現在カメラアングルをコントローラにセット
			CntPtr->SrcAngle = camera_param->Angle.x;
			//パラメータIDが指定する変更パラメータを設定
			CntPtr->DstParam = SmlsParam[inParamID-1].CamParam;
			test_save_work = CntPtr->ParamID;
		}else{					//新カメラからデフォ
			//現在カメラアングルをコントローラにセット
			CntPtr->SrcAngle = SmlsParam[inParamID-1].CamParam.Angle;
			CntPtr->DstParam.Angle = camera_param->Angle.x;
			CntPtr->DstParam.LotDir = 0;
			CntPtr->DstParam.Length = SmlsParam[inParamID-1].CamParam.Length;
			CntPtr->DstParam.Length.x *= -1;
			CntPtr->DstParam.Length.y *= -1;
			CntPtr->DstParam.Length.z *= -1;
			test_save_work = 0;
		}
		CntPtr->TotalSync = SmlsParam[inParamID-1].TotalSync;
		CntPtr->NowSync = 0;
	}
}

//==============================================================================
/**
 * シームレスカメラコントローラメイン処理
 *
 * @param   CntPtr		コントローラポインタ
 *
 * @retval  none
 */
//==============================================================================
void SMLS_CamCnt_Main(SMLS_CAM_CNT_PTR CntPtr)
{
	//リクエストがかかっている場合のみ処理する
	if (CntPtr->Req){
		//変更が・・・
		if (!CntPtr->Reverse){	//通常再生
			OS_Printf("再生\n");
			//シンクの消化
			CntPtr->NowSync++;
			//アングル変更
			ChangeCamAngle( CntPtr->CameraPtr, &CntPtr->SrcAngle, &CntPtr->DstParam.Angle,
					CntPtr->NowSync, CntPtr->TotalSync );
			//シフト
			ShiftCamPos(CntPtr->CameraPtr, &CntPtr->DstParam.Length,
					CntPtr->NowSync, CntPtr->TotalSync );

			if (CntPtr->NowSync >= CntPtr->TotalSync){
				CntPtr->Req = FALSE;
			}
		}else{			//逆再生
			VecFx32 len = CntPtr->DstParam.Length;
			len.x *= (-1);
			len.y *= (-1);
			len.z *= (-1);

			OS_Printf("逆再生\n");
			//アングル変更
			ChangeCamAngle( CntPtr->CameraPtr, &CntPtr->DstParam.Angle, &CntPtr->SrcAngle,
					CntPtr->TotalSync-CntPtr->NowSync, CntPtr->TotalSync );
			//シフト
			ShiftCamPos(CntPtr->CameraPtr, &len,
					CntPtr->NowSync, CntPtr->TotalSync );

			//シンク消化
			CntPtr->NowSync--;
			if (CntPtr->NowSync <= 0){
				//カメラ変更フラグ操作をキャンセル
				if (test_save_work){
					test_save_work = 0;
				}else{
					test_save_work = CntPtr->ParamID;
				}
				CntPtr->Reverse = 0;	//逆再生終了
				CntPtr->Req = FALSE;
			}
		}
	}
}

//アングル変更関数
static void ChangeCamAngle(GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst, const u8 now_sync, const u8 total_sync )
{
	u16 def;
	int tmp;
	CAMERA_ANGLE angle = {0,0,0,0};

	//開始アングルと最終目的アングルの差分を取る
	if ( (*dst) >= (*org)  ){
		def = (*dst) - (*org);
		tmp = (def*now_sync)/total_sync;
	}else{
		def = (*org) - (*dst);
		tmp = -( (def*now_sync)/total_sync);
	}

	//現在アングルの計算
	angle.x = (*org) + tmp;

	//アングルセット
	GFC_SetCameraAngleRev(&angle, camera_ptr);
}

//シフト関数
static void ShiftCamPos(GF_CAMERA_PTR camera_ptr, const VecFx32 *inShift, const u8 now_sync, const u8 total_sync )
{
	VecFx32 vec = {0,0,0};
	//現在アングルの計算
	vec.x = GetAddVal(inShift->x, now_sync, total_sync);
	vec.z = GetAddVal(inShift->z, now_sync, total_sync);

	//シフト
	GFC_ShiftCamera( &vec, camera_ptr);
}

//加算値検出
static fx32 GetAddVal(const fx32 inShiftVal, const u8 now_sync, const u8 total_sync)
{
	fx32 add_val;
	fx32 old, now;
	old = ( inShiftVal * (now_sync-1) ) / total_sync;
	now = ( inShiftVal * now_sync) / total_sync;

	add_val = now - old;

	return add_val;
}
